#ifndef FDC_H
#define FDC_H

#include "main.h"
#include "settings.h"
#include "structs.h"
#include "io.h"
#include "fs.h"
#include "memory.h"
#include "print.h"
#include "panic.h"
#include "scheduler.h"
#include "string.h"
#include "lock.h"

// Benötigte Register des Floppy Laufwerks 
enum floppy_registers {
	FLOPPY_DOR  = 2,  // Digital Output Register
	FLOPPY_MSR  = 4,  // Master Status Register, nur lesend
	FLOPPY_FIFO = 5,  // FIFO
	FLOPPY_CCR  = 7   // Configuration Control Register, nur schreibend
};

// Benötigte Befehle
enum floppy_commands {
	CMD_SPECIFY = 3,            // SPECIFY
	CMD_WRITE_DATA = 5,         // WRITE DATA
	CMD_READ_DATA = 6,          // READ DATA
	CMD_RECALIBRATE = 7,        // RECALIBRATE
	CMD_SENSE_INTERRUPT = 8,    // SENSE INTERRUPT
	CMD_SEEK = 15,              // SEEK
};

// Benötigt von floppy_dma_init und floppy_do_track
typedef enum {
	floppy_dir_read = 1,
	floppy_dir_write = 2
} floppy_dir;

/* Handler Funktion für den Floppy IRQ6
 */
void floppy_handler ();

/* Setzt den IRQ Counter zurück
*/
void floppy_reset_count ();

/* Wartet darauf, dass der IRQ6 x mal aufgerufen wurde
 * Parameter: Zu wartende Aufrufe
 * Rückgaben: Keine
 */
void floppy_wait_irq (int);

/* Zeigt Daten zu eingebauten LWs an
 */
void floppy_detect_drives ();

/* Sendet einen Befehl an den FIFO Port des LWs
 * Parameter: Basisadresse des LWs, Kommandobyte
 * Rückgaben: Keine
 */
void floppy_write_cmd(int, char);

/* Liest ein Byte vom FIFO Port des LWs
 * Parameter: Basisadresse des LWs
 * Rückgaben: Empfangenes Byte
 */
unsigned char floppy_read_data(int);

/* Liest den Status eines LWs
 * Parameter: Basisadresse des LWs, Pointer auf eine Variable für das ST0 Register,
 * 	Pointer auf eine Variable für den aktuellen Zylinder
 * Rückgaben: Keine
 */
void floppy_check_interrupt(int, int *, int *);

/* Kalibriert ein LW, d.h. die Köpfe werden über Zylinder 0 positioniert
 * Parameter: Basisadresse eines LWs
 * Rückgaben: Bei Erfolg 0, ansonsten ungleich 0
 */
int floppy_calibrate(int);

/* Wählt ein Laufwerk zur Verwendung aus
 * Parameter: Laufwerksnummer (0-3)
 * Rückgaben: Keine
 */
void floppy_select_drive(uint8_t);

/* Initialisiert ein LW
 * Parameter: Basisadresse eines LWs
 * Rückgaben: Bei Erfolg TRUE, ansonsten -1
 */
int floppy_reset(int);

/* Schaltet einen Motor an oder aus
 * Parameter: Basisadresse eines LWs, TRUE = anschalten FALSE = abschalten
 * Rückgaben: Keine
 */
void floppy_motor(int, int);

/* Schaltet einen Motor aus ohne irgendwie zu warten
 * Parameter: Basisadresse eines LWs
 * Rückgaben: Keine
 */
void floppy_motor_kill(int);

/* Wartet einige Zeit, bis der Motor abgeschaltet wird. Wird von kernel_leerlauf aufgerufen
 */
void floppy_timer();

/* Platziert einen Kopf über einen bestimmten Zylinder
 * Parameter: Basisadresse eines LWs, Zylinder nummer (0-79), Kopf nummer (0-1)
 * Rückgaben: Bei Erfolg 0, ansonsten ungleich 0
 */
int floppy_seek(int, unsigned int, int);

/* Initialisiert einen DMA Transfer von/zu einem LW
 * Parameter: Richtung (aus dem floppy_dir enum)
 * Rückgaben: Keine
 */
void floppy_dma_init(floppy_dir);

/* Liest oder beschreibt einen 2 Spuren großen Bereich
 * Parameter: Basisadresse eines LWs, Zylinder, Richtung
 * Rückgaben: Bei Erfolg 0, ansonsten ungleich 0
 */
int floppy_do_track(int, unsigned int, floppy_dir);

/* Liest 2 Spuren. Ergbenis im DMA Puffer des LWs
 * Parameter: Basisadresse eines LWs, Zylinder
 * Rückgaben: Bei Erfolg 0, ansonsten ungleich 0
 */
int floppy_read_track(int, unsigned int);

/* Beschreibt 2 Spuren. Eingabepuffer ist der DMA Puffer des LWs
 * Parameter: Basisadresse eines LWs, Zylinder
 * Rückgaben: Bei Erfolg 0, ansonsten ungleich 0
 */
int floppy_write_track(int, unsigned int);

/* Liest einen Sektor und kopiert diesen in den übergebenen Puffer
 * Parameter: Sektornummer in LBA Notation, Puffer[512]
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL floppy_read_sector (uint32_t, void *);

/* Schreibt einen Sektor
 * Parameter: Sektornummer in LBA Notation, Puffer[512]
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL floppy_write_sector (uint32_t, void *);

/* Initialisiert das erste LW und den VFS Treiber
 * Parameter: Keine
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL floppy_init ();

/// VFS Treiber

FILE *fdc_open (const char *, struct fs_entry *, const char *);
size_t fdc_read (FILE *, char *, size_t);
size_t fdc_write (FILE *, void *, size_t);
int fdc_close (FILE *);
int fdc_seek (FILE *, int, int);
size_t fdc_tell (FILE *);
BOOL fdc_init (char);
BOOL fdc_deinit ();

#endif
